﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
#if NET8_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
using System.Linq;
using DimensionsHelper.Common.Utils;

namespace DimensionsHelper.Services.DataCollector;

[DebuggerDisplay("Type = {Type}, Name = {Name}")]
public class ExcelDataNodePathElement(ExcelDataNode node)
{
    public string Type { get; } = node.Type;

    public string Name { get; } = node.Name;

    public ExcelDataNode Node { get; } = node;
}


/// <summary>
///     <see cref="ExcelDataNode.ForEachChild(Action{ExcelDataNode})" />函数使用的回调参数。
/// </summary>
public class ExcelDataNodeCallbackArgs
{
    /// <summary>
    ///     <see cref="ExcelDataNode.ForEachChild(Action{ExcelDataNode})" />函数使用的回调参数。
    /// </summary>
    public ExcelDataNodeCallbackArgs(ExcelDataNode node)
    {
        Node = node;
    }

    /// <summary>
    ///     当前节点。
    /// </summary>
    public ExcelDataNode Node { get; }

    /// <summary>
    ///     如果不要继续遍历，将此属性修改为 true。
    /// </summary>
    public bool IsFinished { get; set; } = false;

    /// <summary>
    ///     如果不需要继续深度遍历，将此属性修改为 true。
    /// </summary>
    public bool IsDfsStopped { get; set; }
}


[Serializable]
[DebuggerDisplay("Type = {Type}, Name = {Name}")]
public class ExcelDataNode
{
    private readonly HashSet<ExcelDataNode> _children = [];
    private readonly Dictionary<string, Dictionary<string, ExcelDataNode>> _childrenMap = new();
    private string _name = string.Empty;
    private ExcelDataNode? _parent;
    private string _type = string.Empty;

    public ExcelDataNode()
    {
    }

    public ExcelDataNode(string type, string name) : this()
    {
        _type = type;
        _name = name;
    }

    public ExcelData Data { get; } = new();

    public ExcelDataNode? Parent
    {
        get => _parent;
        private set => _parent = value;
    }

    /// <summary>
    ///     子节点列表。
    /// </summary>
    public IEnumerable<ExcelDataNode> Children => _children;


    /// <summary>
    ///     子节点数量
    /// </summary>
    public int ChildCount => _children.Count;

    /// <summary>
    ///     子节点包含的类型数量
    /// </summary>
    public int ChildrenTypeCount => _childrenMap.Count;

    public bool IsLeaf => _children.Count == 0;

    public bool IsRoot => _parent is null;

    public bool IsDataNode => !Data.IsEmpty;

    /// <summary>
    ///     当前节点的类型
    /// </summary>
    public string Type
    {
        get => _type;
        private set => _type = value;
    }

    /// <summary>
    ///     当前节点类型名称
    /// </summary>
    public string Name
    {
        get => _name;
        private set => _name = value;
    }

    /// <summary>
    ///     对每一个子节点执行回调函数。
    /// </summary>
    /// <param name="callback"></param>
    public void ForEachChild(Action<ExcelDataNode> callback)
    {
        foreach (var pair in _childrenMap)
        {
            foreach (var pair2 in pair.Value)
            {
                callback(pair2.Value);
            }
        }
    }

    /// <summary>
    ///     获取当前节点的父级节点路径，顺序是：根节点->此节点，包含此节点。
    /// </summary>
    /// <param name="includeRoot"></param>
    /// <returns></returns>
    public List<ExcelDataNodePathElement> GetPath(bool includeRoot = false)
    {
        var path = new List<ExcelDataNodePathElement>();
        var node = this;

        while (node != null && (includeRoot || !node.IsRoot))
        {
            path.Add(new ExcelDataNodePathElement(node));
            node = node.Parent;
        }

        path.Reverse();
        return path;
    }

    public bool TryGetChild(string type, string name, 
#if NET8_0_OR_GREATER
        [MaybeNullWhen(false)]
#endif
        out ExcelDataNode child)
    {
        child = null!;
        return _childrenMap.TryGetValue(type, out var nameMap) &&
               nameMap.TryGetValue(name, out child);
    }

    /// <summary>
    ///     获取指定类型和名称的子节点。
    /// </summary>
    /// <param name="type">指定类型</param>
    /// <param name="name">指定类型名</param>
    /// <returns>如果不存在，将返回null</returns>
    public ExcelDataNode? GetChild(string type, string name)
    {
        if (_childrenMap.TryGetValue(type, out var nameMap) &&
            nameMap.TryGetValue(name, out var node))
        {
            return node;
        }

        return null;
    }

    /// <summary>
    ///     创建或获取指定路径的子节点
    /// </summary>
    /// <param name="path">节点路径</param>
    public ExcelDataNode GetChild(IList<ExcelDataNodePathElement> path)
    {
        return path.Aggregate(this,
            (current, pathElement) => current.GetChild(pathElement.Type, pathElement.Name) ??
                                      current.AddChild(pathElement.Type, pathElement.Name));
    }

    /// <summary>
    ///     创建或获取指定路径的子节点
    /// </summary>
    /// <param name="path">节点路径</param>
    public ExcelDataNode GetChild(IList<KeyValuePair<string, string>> path)
    {
        var node = this;

        foreach (var pair in path)
        {
            node = node.GetChild(pair.Key, pair.Value) ?? node.AddChild(pair.Key, pair.Value);
        }

        return node;
    }


    /// <summary>
    ///     查找第一个符合断言回调的子节点
    /// </summary>
    /// <param name="predicate">对每个子节点调用的回调</param>
    public ExcelDataNode? FindChild(Predicate<ExcelDataNode> predicate)
    {
        return _children.FirstOrDefault(child => predicate(child));
    }

    /// <summary>
    ///     添加一个指定类型和指定类型名的子节点，如果不存在，将创建一个新的。
    /// </summary>
    /// <param name="type">指定类型</param>
    /// <param name="name">指定类型名</param>
    /// <returns></returns>
    public ExcelDataNode AddChild(string type, string name)
    {
        var node = new ExcelDataNode(type, name)
        {
            Parent = this
        };

        _children.Add(node);

        if (!_childrenMap.TryGetValue(type, out var nameMap))
        {
            nameMap = new Dictionary<string, ExcelDataNode>();
            _childrenMap.Add(type, nameMap);
        }

        nameMap.SetValue(name, node);

        return node;
    }


    private void AddChild(ExcelDataNode node)
    {
        node.Parent = this;
        if (!_childrenMap.TryGetValue(node.Type, out var nameMap))
        {
            nameMap = new Dictionary<string, ExcelDataNode>();
            _childrenMap.Add(node.Type, nameMap);
        }

        nameMap.SetValue(node.Name, node);
        _children.Add(node);
    }


    private void RemoveChild(ExcelDataNode node)
    {
        if (!_children.Remove(node))
        {
            return;
        }

        var map = _childrenMap[node.Type];
        map.Remove(node.Name);

        if (map.Count == 0)
        {
            _childrenMap.Remove(node.Type);
        }
    }


    /// <summary>
    ///     向子节点集合中添加一个新的子节点，同时将所有不是同类型的节点添加到此节点的子节点列表中。
    /// </summary>
    /// <param name="type">指定类型</param>
    /// <param name="name">指定类型名</param>
    /// <returns></returns>
    public ExcelDataNode InsertChild(string type, string name)
    {
        if (_childrenMap.TryGetValue(type, out var vals) && vals.ContainsKey(name))
        {
            throw new Exception($"子节点中已有类型是'{type}'，值是'{name}'的节点。");
        }

        var node = new ExcelDataNode(type, name) { Parent = this };
        foreach (var child in Children.Where(child => child.Type != type))
        {
            node.AddChild(child);
            RemoveChild(child);
            AddChild(node);
        }

        return node;
    }


    /// <summary>
    ///     如果子节点中存在给定类型，将返回true
    /// </summary>
    /// <param name="type">指定类型</param>
    public bool ContainsType(string type)
    {
        return _childrenMap.ContainsKey(type);
    }


    /// <summary>
    ///     返回指定类型在子节点中的出现次数。
    /// </summary>
    /// <param name="type">指定类型</param>
    public int CountType(string type)
    {
        return _childrenMap.TryGetValue(type, out var nameMap) ? nameMap.Count : 0;
    }


    /// <summary>
    ///     筛选指定类型的子节点。
    /// </summary>
    /// <param name="type">指定类型</param>
    public List<ExcelDataNode> Filter(string type)
    {
        var result = new List<ExcelDataNode>();

        if (_childrenMap.TryGetValue(type, out var nameMap))
        {
            result.AddRange(nameMap.Values);
        }

        return result;
    }

    /// <summary>
    ///     递归遍历node的所有子节点，深度优先。
    /// </summary>
    /// <param name="node">给定根节点，此节点不会调用<paramref name="callback" /></param>
    /// <param name="callback">针对每个节点调用的回调函数</param>
    public static void ForEachChild(ExcelDataNode node, Action<ExcelDataNodeCallbackArgs> callback)
    {
        foreach (var childNode in node.Children)
        {
            var args = new ExcelDataNodeCallbackArgs(childNode);
            callback.Invoke(args);

            if (args.IsFinished)
            {
                return;
            }

            if (!args.IsDfsStopped && childNode.ChildCount > 0)
            {
                ForEachChild(childNode, callback);
            }
        }
    }

    /// <summary>
    ///     获取当前节点下的所有保存数据的节点，如果当前节点是数据节点，列表中将只有当前节点一个对象。
    /// </summary>
    public List<ExcelDataNode> GetDataChildren()
    {
        var list = new List<ExcelDataNode>();

        if (IsDataNode)
        {
            list.Add(this);
        }
        else
        {
            ForEachChild(this, args =>
            {
                if (args.Node.IsDataNode)
                {
                    list.Add(args.Node);
                }
            });
        }

        return list;
    }
}